﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Snowball;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Highlight;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using RapidWebDev.Common;
using RapidWebDev.SearchIndex.Contract;

namespace RapidWebDev.SearchIndex
{
	/// <summary>
	/// The service to search indexed documents.
	/// </summary>
	public class SearchService : ISearchService
	{
		private ILuceneAnalyzerResolver luceneAnalyzerResolver;
		private string rootIndexDirectory;
		private const int MIN_HIGHLIGHT_FRAGMENT_TOKEN_NUMBER = 20;
		private static readonly HashSet<SearchFilterOperators> searchFilterOperatorsForHighlight = new HashSet<SearchFilterOperators>
		{
			SearchFilterOperators.All,
			SearchFilterOperators.Any,
			SearchFilterOperators.Like,
		};

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="luceneAnalyzerResolver"></param>
		public SearchService(ILuceneAnalyzerResolver luceneAnalyzerResolver)
		{
			SearchIndexConfigurationSection searchIndexConfigurationSection = ConfigurationManager.GetSection("searchIndex") as SearchIndexConfigurationSection;
			if (searchIndexConfigurationSection == null)
				throw new ConfigurationErrorsException("The configuration section \"searchIndex\" is not configured in application configuration file.");

			this.rootIndexDirectory = Kit.ToAbsolutePath(searchIndexConfigurationSection.RootIndexDirectory);
			this.luceneAnalyzerResolver = luceneAnalyzerResolver;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		public SearchService()
		{
			SearchIndexConfigurationSection searchIndexConfigurationSection = ConfigurationManager.GetSection("searchIndex") as SearchIndexConfigurationSection;
			if (searchIndexConfigurationSection == null)
				throw new ConfigurationErrorsException("The configuration section \"searchIndex\" is not configured in application configuration file.");

			this.rootIndexDirectory = Kit.ToAbsolutePath(searchIndexConfigurationSection.RootIndexDirectory);
			this.luceneAnalyzerResolver = SpringContext.Current.GetObject<ILuceneAnalyzerResolver>();
		}

		/// <summary>
		/// Search documents by parameters.
		/// </summary>
		/// <param name="searchParameter"></param>
		/// <returns></returns>
		public SearchResultMessage Search(SearchParameterMessage searchParameter)
		{
			ValidateSearchParameter(searchParameter);

			Query queryToRun = CreateLuceneQuery(searchParameter);
			Sort sort = CreateLuceneSort(searchParameter);

			SearchResultMessage searchResult = new SearchResultMessage
			{
				ApplicationId = searchParameter.ApplicationId,
				PageIndex = searchParameter.PageIndex,
				PageSize = searchParameter.PageSize,
				ApproximateTotalHits = 0,
				AggregateResults = new Collection<AggregateResultMessage>(),
				Docs = new Collection<SearchResultDocMessage>()
			};

			FSDirectory indexDirectory = this.ResolveIndexDirectory(searchParameter.ApplicationId);
			if (indexDirectory == null) return searchResult;

			bool usingAggregate = searchParameter.AggregateParameters != null && searchParameter.AggregateParameters.Count > 0;

			IndexSearcher searcher = null;
			try
			{
				searcher = new IndexSearcher(indexDirectory, true);
				// if the client wants for aggregate, here has to go through all docs.
				int topDocsNumber = !usingAggregate ? searchParameter.PageSize * (searchParameter.PageIndex + 1) : searcher.MaxDoc();
				int startDocIndex = searchParameter.PageSize * searchParameter.PageIndex;

				TopFieldDocs topFieldDocs = searcher.Search(queryToRun, null, topDocsNumber, sort);
				searchResult.ApproximateTotalHits = topFieldDocs.totalHits;

				if (usingAggregate)
					searchResult.AggregateResults = SearchAggregateApi.Analyze(searchParameter.ApplicationId, searcher, topFieldDocs, searchParameter.AggregateParameters);

				Dictionary<string, Highlighter> highlighterByFieldNameDictionary = new Dictionary<string, Highlighter>();
				for (int i = startDocIndex; i < topFieldDocs.scoreDocs.Length; i++)
				{
					Document doc = searcher.Doc(topFieldDocs.scoreDocs[i].doc);
					SearchResultDocMessage searchResultDoc = CreateSearchResultDocument(doc, searchParameter, highlighterByFieldNameDictionary);
					searchResult.Docs.Add(searchResultDoc);
				}

				return searchResult;
			}
			catch (Exception exp)
			{
				Logger.Instance(this).Error(exp);
				throw;
			}
			finally
			{
				searcher.Close();
				indexDirectory.Close();
			}
		}

		/// <summary>
		/// Aggregate the searched documents by the parameter.
		/// </summary>
		/// <param name="aggregateParameter"></param>
		/// <returns></returns>
		public AggregateResultMessage Aggregate(AggregateParameterMessage aggregateParameter)
		{
			Kit.NotNull(aggregateParameter, "aggregateParameter");
			Kit.NotNull(aggregateParameter.ApplicationId, "aggregateParameter.ApplicationId");

			Query queryToRun = CreateLuceneQuery(aggregateParameter);

			AggregateResultMessage aggregateResult = new AggregateResultMessage
			{
				ApplicationId = aggregateParameter.ApplicationId,
				FieldName = aggregateParameter.FieldName,
				Values = new Collection<AggregateFieldResultMessage>()
			};

			FSDirectory indexDirectory = this.ResolveIndexDirectory(aggregateParameter.ApplicationId);
			if (indexDirectory == null) return aggregateResult;

			IndexSearcher searcher = null;
			try
			{
				searcher = new IndexSearcher(indexDirectory, true);
				TopFieldDocs topFieldDocs = searcher.Search(queryToRun, null, searcher.MaxDoc(), Sort.INDEXORDER);
				SearchWithAggregateFieldMessage searchWithAggregateField = new SearchWithAggregateFieldMessage
				{
					FieldName = aggregateParameter.FieldName
				};

				Collection<AggregateResultMessage> aggregateResultsCollection = SearchAggregateApi.Analyze(aggregateParameter.ApplicationId, searcher, topFieldDocs, new[] { searchWithAggregateField });
				if (aggregateResultsCollection.Count > 0)
					return aggregateResultsCollection[0];

				return aggregateResult;
			}
			catch (Exception exp)
			{
				Logger.Instance(this).Error(exp);
				throw;
			}
			finally
			{
				searcher.Close();
				indexDirectory.Close();
			}
		}

		private FSDirectory ResolveIndexDirectory(string applicationId)
		{
			string indexDirectoryPath = Path.Combine(this.rootIndexDirectory, applicationId.ToString());
			if (System.IO.Directory.Exists(indexDirectoryPath))
				return FSDirectory.Open(new System.IO.DirectoryInfo(indexDirectoryPath));

			return null;
		}

		private Query CreateLuceneQuery(SearchParameterMessage searchParameter)
		{
			BooleanQuery queryToRun = new BooleanQuery();

			bool hasSearchQuery = false;
			hasSearchQuery = AddDocumentTypeQuery(searchParameter.DocumentTypes, queryToRun) || hasSearchQuery;
			hasSearchQuery = AddKeywordQuery(searchParameter.Keyword, searchParameter.StemmingLanguage, queryToRun) || hasSearchQuery;
			hasSearchQuery = AddSearchFilterQuery(searchParameter.Filters, searchParameter.StemmingLanguage, queryToRun, null) || hasSearchQuery;

			if (hasSearchQuery)
				return queryToRun;

			return new MatchAllDocsQuery();
		}

		private Query CreateLuceneQuery(AggregateParameterMessage aggregateParameter)
		{
			BooleanQuery queryToRun = new BooleanQuery();

			bool hasSearchQuery = false;
			hasSearchQuery = AddDocumentTypeQuery(aggregateParameter.DocumentTypes, queryToRun) || hasSearchQuery;
			hasSearchQuery = AddKeywordQuery(aggregateParameter.Keyword, aggregateParameter.StemmingLanguage, queryToRun) || hasSearchQuery;
			hasSearchQuery = AddSearchFilterQuery(aggregateParameter.Filters, aggregateParameter.StemmingLanguage, queryToRun, null) || hasSearchQuery;

			if (hasSearchQuery)
				return queryToRun;

			return new MatchAllDocsQuery();
		}

		private static bool AddDocumentTypeQuery(Collection<string> documentTypes, BooleanQuery queryToRun)
		{
			if (documentTypes != null && documentTypes.Count > 0)
			{
				BooleanQuery documentTypeQuery = new BooleanQuery();
				foreach (string documentType in documentTypes)
				{
					Term documentTypeTerm = new Term(LuceneFields.DocumentType, documentType);
					TermQuery documentTypeTermQuery = new TermQuery(documentTypeTerm);
					documentTypeQuery.Add(documentTypeTermQuery, BooleanClause.Occur.SHOULD);
				}

				queryToRun.Add(documentTypeQuery, BooleanClause.Occur.MUST);
				return true;
			}

			return false;
		}

		private bool AddKeywordQuery(string keyword, string stemmingLanguage, BooleanQuery queryToRun)
		{
			if (!string.IsNullOrEmpty(keyword))
			{
				Analyzer analyzer = this.luceneAnalyzerResolver.Resolve(IndexDocFieldValueTypes.Text, stemmingLanguage);
				QueryParser queryParser = new QueryParser(Lucene.Net.Util.Version.LUCENE_CURRENT, LuceneFields.Content, analyzer);
				Query keywordQuery = queryParser.Parse(keyword);
				queryToRun.Add(keywordQuery, BooleanClause.Occur.MUST);
				return true;
			}

			return false;
		}

		private static bool AddSearchFilterQuery(Collection<SearchFilterMessage> searchFilters, string stemmingLanguage, BooleanQuery queryToRun, Func<IEnumerable<SearchFilterMessage>, IEnumerable<SearchFilterMessage>> searchFiltersCallback)
		{
			if (searchFilters == null) return false;
			IEnumerable<SearchFilterMessage> searchFiltersResult = searchFilters;
			if (searchFiltersCallback != null)
				searchFiltersResult = searchFiltersCallback(searchFilters);
			if (searchFiltersResult == null || searchFiltersResult.Count() == 0) return false;

			foreach (SearchFilterMessage searchFilter in searchFiltersResult)
			{
				IDocumentField documentField = DocumentFieldFactory.Create(searchFilter.FieldType);
				if (documentField == null)
					throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, @"The FieldType ""{0}"" of search filter is invalid.", searchFilter.FieldType));

				documentField.AppendQuery(queryToRun, searchFilter, stemmingLanguage);
			}

			return true;
		}

		private static Sort CreateLuceneSort(SearchParameterMessage searchParameter)
		{
			if (searchParameter.SortingParameter == null || searchParameter.SortingParameter.Count == 0)
				return Sort.RELEVANCE;

			List<SortField> sortFields = new List<SortField>();
			foreach (SearchSortingParameterMessage searchSortingParameter in searchParameter.SortingParameter)
			{
				if (searchSortingParameter != null && !searchSortingParameter.UseRelevance)
					sortFields.Add(new SortField(LuceneFields.SortableFieldName(searchSortingParameter.FieldName), GetSortFieldType(searchSortingParameter), !searchSortingParameter.Ascending));
				else
					sortFields.Add(new SortField(null, SortField.SCORE, false));
			}

			return new Sort(sortFields.ToArray());
		}

		private SearchResultDocMessage CreateSearchResultDocument(Document doc, SearchParameterMessage searchParameter, Dictionary<string, Highlighter> highlighterByFieldNameDictionary)
		{
			SearchResultDocMessage searchResultDoc = new SearchResultDocMessage();
			BuildSearchResultDocAttachments(doc, searchResultDoc, searchParameter.Selector);
			BuildSearchResultDocFields(doc, searchResultDoc, searchParameter.Selector);

			// highlight for SearchResultDocMessage.HighlightFragment
			const string genericHighlighterName = "#GenericHighlighter#";
			if (!highlighterByFieldNameDictionary.ContainsKey(genericHighlighterName))
				highlighterByFieldNameDictionary[genericHighlighterName] = CreateGenericHighlighter(searchParameter);

			Highlighter highlighter = highlighterByFieldNameDictionary[genericHighlighterName];
			BuildSearchResultDocHighlightFragment(doc, searchResultDoc, searchParameter, highlighter);

			// highlight for each field of SearchResultDocMessage
			BuildSearchResultDocFieldHighlightFragment(doc, searchResultDoc, searchParameter, highlighterByFieldNameDictionary);

			return searchResultDoc;
		}

		private static void BuildSearchResultDocAttachments(Document doc, SearchResultDocMessage searchResultDoc, SearchResultSelectorMessage searchResultSelector)
		{
			if (searchResultSelector == null) return;
			if (!searchResultSelector.ReturnDocAttachments) return;

			searchResultDoc.Attachments = new Collection<SearchResultDocAttachmentMessage>();
			string attachmentsCountText = doc.Get(LuceneFields.AttachmentsCount);
			int attachmentsCount = 0;
			if (int.TryParse(attachmentsCountText, out attachmentsCount))
			{
				for (int i = 0; i < attachmentsCount; i++)
				{
					string attachmentId = doc.Get(LuceneFields.AttachmentIdField(i));
					string attachmentName = doc.Get(LuceneFields.AttachmentNameField(i));
					string attachmentSharedPath = doc.Get(LuceneFields.AttachmentSharedPathField(i));
					if (!string.IsNullOrEmpty(attachmentId) && !string.IsNullOrEmpty(attachmentName))
					{
						// don't have highlight fragment on attachment now.
						searchResultDoc.Attachments.Add(new SearchResultDocAttachmentMessage
						{
							Id = attachmentId,
							Name = attachmentName,
							SharedPath = attachmentSharedPath
						});
					}
				}
			}
		}

		private static void BuildSearchResultDocFields(Document doc, SearchResultDocMessage searchResultDoc, SearchResultSelectorMessage searchResultSelector)
		{
			if (searchResultSelector == null) return;

			bool returnAllFields = searchResultSelector.ReturnOnlySpecialFields == null;
			HashSet<string> returnFieldsHashSet = null;
			if (searchResultSelector.ReturnOnlySpecialFields != null && searchResultSelector.ReturnOnlySpecialFields.Count > 0)
				returnFieldsHashSet = new HashSet<string>(searchResultSelector.ReturnOnlySpecialFields.Select(f => f.FieldName).Distinct());

			searchResultDoc.Id = doc.Get(LuceneFields.DocumentId);
			searchResultDoc.Type = doc.Get(LuceneFields.DocumentType);
			try
			{
				searchResultDoc.IndexedOn = DateTools.StringToDate(doc.Get(LuceneFields.UpdatedOn));
			}
			catch
			{
			}

			if (returnAllFields || returnFieldsHashSet != null)
			{
				Dictionary<string, SearchResultDocFieldMessage> searchResultDocFields = new Dictionary<string, SearchResultDocFieldMessage>(StringComparer.OrdinalIgnoreCase);
				foreach (Field field in doc.GetFields())
				{
					string fieldName = field.Name();
					if (returnAllFields || returnFieldsHashSet.Contains(fieldName))
					{
						if (LuceneFields.IsPredefinedField(fieldName)) continue;

						SearchResultDocFieldMessage searchResultDocField = null;
						if (!searchResultDocFields.ContainsKey(fieldName))
						{
							searchResultDocField = new SearchResultDocFieldMessage { Name = fieldName };
							searchResultDocFields[fieldName] = searchResultDocField;
							searchResultDocField.Values = new Collection<string>();

							try
							{
								string fieldTypeFieldName = LuceneFields.FieldType(fieldName);
								string fieldTypeText = doc.Get(fieldTypeFieldName);
								searchResultDocField.Type = (IndexDocFieldValueTypes)Enum.Parse(typeof(IndexDocFieldValueTypes), fieldTypeText);
							}
							catch (Exception exp)
							{
								Logger.Instance(typeof(SearchService)).Warn(exp);
							}
						}

						searchResultDocField = searchResultDocFields[fieldName];
						string luceneStoredFieldValue = field.StringValue();
						IDocumentField documentField = DocumentFieldFactory.Create(searchResultDocField.Type);
						if (documentField == null) continue;
						string originalFieldValue = documentField.ResolveOriginalFieldValue(luceneStoredFieldValue);
						if (originalFieldValue != null)
							searchResultDocField.Values.Add(originalFieldValue.ToString());
					}
				}

				List<SearchResultDocFieldMessage> searchResultDocFieldsSortable = (from searchResultDocField in searchResultDocFields
																				   orderby searchResultDocField.Key
																				   select searchResultDocField.Value).ToList();
				searchResultDoc.Fields = new Collection<SearchResultDocFieldMessage>(searchResultDocFieldsSortable);
			}
		}

		private void BuildSearchResultDocHighlightFragment(Document doc, SearchResultDocMessage searchResultDoc, SearchParameterMessage searchParameter, Highlighter highlighter)
		{
			if (highlighter == null) return;
			if (searchParameter.Selector == null) return;
			if (!searchParameter.Selector.ReturnKeywordHighlightFragment) return;

			string stemmingLanguage = doc.Get(LuceneFields.StemmingLanguage);
			Analyzer analyzer = this.luceneAnalyzerResolver.Resolve(IndexDocFieldValueTypes.Text, stemmingLanguage);
			string[] fieldValues = doc.GetValues(LuceneFields.Content);
			foreach (string fieldValue in fieldValues)
			{
				if (string.IsNullOrEmpty(fieldValue)) continue;
				using (StringReader stringReader = new StringReader(fieldValue))
				{
					TokenStream tokenStream = analyzer.TokenStream(LuceneFields.Content, stringReader);
					string highlightFragment = highlighter.GetBestFragment(tokenStream, fieldValue);
					if (!string.IsNullOrEmpty(highlightFragment))
					{
						searchResultDoc.HighlightFragment = highlightFragment;
						return;
					}
				}
			}
		}

		private void BuildSearchResultDocFieldHighlightFragment(Document doc, SearchResultDocMessage searchResultDoc, SearchParameterMessage searchParameter, Dictionary<string, Highlighter> highlighterByFieldNameDictionary)
		{
			if (searchParameter.Selector == null) return;
			if (searchParameter.Selector.ReturnOnlySpecialFields == null) return;

			List<string> highlightFieldNames = (from f in searchParameter.Selector.ReturnOnlySpecialFields
											  where f.EnableHighlightFragment
											  select f.FieldName).Distinct().ToList();
			if (highlightFieldNames.Count == 0) return;

			foreach (string highlightFieldName in highlightFieldNames)
			{
				if (!highlighterByFieldNameDictionary.ContainsKey(highlightFieldName))
					highlighterByFieldNameDictionary[highlightFieldName] = CreateFieldHighlighter(searchParameter, highlightFieldName);

				Highlighter highlighter = highlighterByFieldNameDictionary[highlightFieldName];
				if (highlighter == null) continue;

				SearchResultDocFieldMessage searchResultDocField = searchResultDoc.Fields.FirstOrDefault(f => f.Name == highlightFieldName);
				if (searchResultDocField == null) continue;
				if (searchResultDocField.Type != IndexDocFieldValueTypes.Text) continue;

				string stemmingLanguage = doc.Get(LuceneFields.StemmingLanguage);
				Analyzer analyzer = this.luceneAnalyzerResolver.Resolve(searchResultDocField.Type, stemmingLanguage);
				Collection<string> highlightFieldValues = new Collection<string>();
				foreach (string fieldValue in searchResultDocField.Values)
				{
					if (string.IsNullOrEmpty(fieldValue)) continue;
					using (StringReader stringReader = new StringReader(fieldValue))
					{
						TokenStream tokenStream = analyzer.TokenStream(highlightFieldName, stringReader);
						string highlightFragment = highlighter.GetBestFragment(tokenStream, fieldValue);
						if (string.IsNullOrEmpty(highlightFragment))
							highlightFieldValues.Add(fieldValue);
						else
							highlightFieldValues.Add(highlightFragment);
					}
				}

				searchResultDocField.Values = highlightFieldValues;
			}
		}

		private Highlighter CreateGenericHighlighter(SearchParameterMessage searchParameter)
		{
			BooleanQuery queryToHighlighter = new BooleanQuery();
			bool hasSearchQuery = AddKeywordQuery(searchParameter.Keyword, searchParameter.StemmingLanguage, queryToHighlighter);
			hasSearchQuery = AddSearchFilterQuery(searchParameter.Filters, searchParameter.StemmingLanguage, queryToHighlighter, searchFilters =>
				{
					return (from f in searchFilters
							where searchFilterOperatorsForHighlight.Contains(f.Operator)
							select f).ToList();
				}) || hasSearchQuery;

			if (!hasSearchQuery) return null;

			QueryScorer queryScorer = new QueryScorer(queryToHighlighter);
			SimpleHTMLFormatter formatter = new SimpleHTMLFormatter(SearchHighlighter.StartHighlightMarkup, SearchHighlighter.EndHighlightMarkup);
			return new Highlighter(formatter, new DefaultEncoder(), queryScorer);
		}

		private Highlighter CreateFieldHighlighter(SearchParameterMessage searchParameter, string highlightFieldName)
		{
			if (searchParameter.Selector == null) return null;
			if (searchParameter.Selector.ReturnOnlySpecialFields == null) return null;
			List<string> highlightFieldNameHashSet = new List<string>((from f in searchParameter.Selector.ReturnOnlySpecialFields
																	where f.EnableHighlightFragment
																	select f.FieldName).Distinct());
			if (highlightFieldNameHashSet.Count == 0) return null;

			BooleanQuery queryToHighlighter = new BooleanQuery();
			bool hasSearchQuery = AddKeywordQuery(searchParameter.Keyword, searchParameter.StemmingLanguage, queryToHighlighter);
			hasSearchQuery = AddSearchFilterQuery(searchParameter.Filters, searchParameter.StemmingLanguage, queryToHighlighter, searchFilters =>
			{
				return (from f in searchFilters
						where searchFilterOperatorsForHighlight.Contains(f.Operator) && f.FieldName == highlightFieldName
						select f).ToList();
			}) || hasSearchQuery;

			if (!hasSearchQuery) return null;

			QueryScorer queryScorer = new QueryScorer(queryToHighlighter);
			SimpleHTMLFormatter formatter = new SimpleHTMLFormatter(SearchHighlighter.StartHighlightMarkup, SearchHighlighter.EndHighlightMarkup);
			Highlighter highlighter = new Highlighter(formatter, new DefaultEncoder(), queryScorer);
			highlighter.SetMaxDocBytesToAnalyze(10240);
			highlighter.SetTextFragmenter(new SimpleFragmenter(10240));
			return highlighter;
		}

		private static void ValidateSearchParameter(SearchParameterMessage searchParameter)
		{
			Kit.NotNull(searchParameter, "searchParameter");
			Kit.NotNull(searchParameter.ApplicationId, "searchParameter.ApplicationId");
			if (searchParameter.PageSize <= 0)
				throw new ArgumentException("The property PageSize should be more than zero.", "searchParameter");
			if (searchParameter.PageIndex < 0)
				throw new ArgumentException("The property PageIndex should be more than zero.", "searchParameter");
			if (searchParameter.Selector != null && searchParameter.Selector.ReturnOnlySpecialFields != null)
			{
				int invalidFieldNameNumber = (from f in searchParameter.Selector.ReturnOnlySpecialFields
											  where string.IsNullOrEmpty(f.FieldName)
											  select f).Count();
				if (invalidFieldNameNumber > 0)
					throw new ArgumentException("The FieldName of field selector should not be empty or null.", "searchParameter");
			}
		}

		private static int GetSortFieldType(SearchSortingParameterMessage searchSortingParameter)
		{
			switch (searchSortingParameter.FieldType)
			{
				case IndexDocFieldValueTypes.Decimal:
					return SortField.FLOAT;
				case IndexDocFieldValueTypes.Integer:
					return SortField.INT;
				default:
					return SortField.STRING;
			}
		}
	}
}